/**
 *  Version:     @(#)aio_slot.h    0.2.11 15/06/2007
 *
 *  Authors:     Jinyuan Hei <heijinyuan@hotmail.com>
 *               Hailong Xia <hlxxxx@gmail.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty
 *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *  the GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General
 *  Public License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *  Boston, MA 02111-1307  USA
 */

#ifndef __AIO_SLOT_H
#define __AIO_SLOT_H

/** 
 * AIO slot is a mininum unit of operate asynchronous event.
 * There are many slots in an AIO base. Each slot was associated 
 * with socket file descriptor. 
 */

#include "aio_base.h"

#ifdef _AIO_USE_OPENSSL
#include <openssl/ssl.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

#ifndef AIO_SLOT_RECVBUF_OFFSET
#define AIO_SLOT_RECVBUF_OFFSET     0x00000000 
#endif

#ifndef AIO_SLOT_SENDQUE_CAPABILITY
#define AIO_SLOT_SENDQUE_CAPABILITY 0x00000010 
#endif

#ifndef AIO_DEFAULT_BACKLOG
#define AIO_DEFAULT_BACKLOG         500
#endif

#define SLOTABP(slot)               ((slot)->ab)
#define SLOTEBP(slot)               ((slot)->ab->eb)
#define SLOTTYPE(slot)              ((slot)->type)
#define SLOTFD(slot)                ((slot)->fd)
#define SLOTBINDIP(slot)            ((slot)->bindip)
#define SLOTBINDPORT(slot)          ((slot)->bindport)
#define SLOTDSTIP(slot)             ((slot)->dstip)
#define SLOTDSTPORT(slot)           ((slot)->dstport)
#define SLOTFRMIP(slot)             ((slot)->frmip)
#define SLOTFRMPORT(slot)           ((slot)->frmport)
#define SLOTIOERR(slot)             ((slot)->io_errno)
#define SLOTUSRP(slot)              ((slot)->usrpointer)
#define SLOTASSIGN(slot)            ((slot)->refcnt++, (slot))

#define AIO_RAW                     0x10000000
#define AIO_STREAM                  0x01000000

enum slot_type { 
        AIO_UDP     =               0x00000001,
        AIO_FILE    =               0x01000001,
        AIO_TCP     =               0x01000010,
        AIO_TLS     =               0x01000100,
        AIO_ICMP    =               0x10000001,
        AIO_IGMP    =               0x10000010,
        AIO_RAW_UDP =               0x10000100, /* Only for send custom ip header's udp packet */
        AIO_RAW_TCP =               0x10001000, /* Only for send custom ip header's tcp packet */
};

struct slot_sq {
        struct aio_buf              *sb;
        struct slot_sq              *next;
        struct slot_sq              *tail;
        unsigned int                len;
};

struct aio_slot {
        unsigned int                refcnt;
        struct aio_base             *ab;
        enum slot_type              type;
        int                         fd;

        unsigned long               bindip;     /* network bytes order */
        unsigned long               dstip;      /* network bytes order */
        unsigned long               frmip;      /* network bytes order */

        unsigned short              bindport;   /* network bytes order */
        unsigned short              dstport;    /* network bytes order */
        unsigned short              frmport;    /* network bytes order */
        unsigned short              sqcapability;

        unsigned                    bytestoread;
        void                        *usrpointer;

        AIO_EVENT                   rev;
        AIO_EVENT                   wev;
        AIO_EVENT                   eev;

        struct timeval              etv;

        void                        (*on_accepted)(struct aio_slot *);
        void                        (*on_connected)(struct aio_slot *);
        void                        (*on_received)(struct aio_slot *);
        void                        (*on_sent)(struct aio_slot *);
        void                        (*on_closed)(struct aio_slot *);

        struct slot_sq              sq;
        struct aio_buf              *rb;

        unsigned l_listening        :1;
        unsigned l_connecting       :1;
        unsigned l_connected        :1;
        unsigned l_permanence       :1;
        unsigned l_timeout          :1;
        unsigned l_close            :1;
        unsigned l_write            :1;
        unsigned l_read             :1;

        short                       io_errno;
        LIST_ENTRY(aio_slot)        entries;

#ifdef _AIO_USE_OPENSSL
        SSL_CTX                     *ctx;
        SSL                         *ssl;
        unsigned char               authrequire;
#endif
};

#define AIO_SETDSTADDR(slot, ip/*char* */, port/*host order */)\
        (slot)->dstip = inet_addr(ip); (slot)->dstport = htons(port); 

#define AIO_SETDSTADDR2(slot, ip/*network order*/, port/*host order*/)\
        (slot)->dstip = (ip); (slot)->dstport = htons(port)                 

#define AIO_SETDSTADDR3(slot, ip/*network order */, port/*network order*/)\
        (slot)->dstip = (ip); (slot)->dstport = (port)       

#define AIO_SETSENDQUE(slot, capability)                      \
        (slot)->sqcapability = (capability)

#define AIO_SETTORREAD(slot, length)                          \
        ((length) <= 0 || (length) > MAX_RECVBUF_LEN) ? -1 :  \
        (slot)->bytestoread = (length)

#define AIO_READ(slot, autorm)                                \
        (slot)->rb;                                           \
        do {                                                  \
                if (autorm) (slot)->rb = 0;                   \
        } while (0)

/* internal function */
void datagram_send(struct aio_slot *slot, struct aio_buf *sb);
void stream_send(struct aio_slot *slot, struct aio_buf *sb);

#define AIO_WRITE(slot, sb, fn)                               \
do {                                                          \
        if ((slot)->l_close) {                                \
                AIO_BUF_FREE((sb));                           \
        } else {                                              \
                (slot)->on_sent = fn;                         \
                ((slot)->type & AIO_STREAM) ?                 \
                stream_send((slot), (sb)) :                   \
                datagram_send((slot), (sb));                  \
        }                                                     \
} while (0)

/** 
 * If port don't equal 0, the function will bind socket address. 
 */
struct aio_slot* aio_slot_open(struct aio_base *ab, 
        const char *ip, unsigned short port, enum slot_type type);

/** 
 * The argument ip is in network bytes order. If the argument port 
 * don't equal 0, the function will bind socket address. 
 */
struct aio_slot* aio_slot_open2(struct aio_base *ab, 
        unsigned long ip, unsigned short port, enum slot_type type);

/** 
 * The aio_slot_open3() will be called when there is a exist fd 
 * This function have not responsibility to bind socket address.
 * If you want do it, you must be manual call bind() function.
 */
struct aio_slot * aio_slot_open3(struct aio_base *ab, int fd, 
        unsigned long ip, unsigned short port, enum slot_type type);

/**
 * This function will free slot pointer. So you don't continue use
 * the slot after you invoke aio_slot_close on it 
 */
void aio_slot_close(struct aio_slot *slot, int fatal);

int aio_getttl(struct aio_slot *slot, int *ttl);

int aio_setttl(struct aio_slot *slot, int ttl);

void aio_setsendque(struct aio_slot *slot, int capability);

/* The argument port is in host bytes order */
void aio_setdstaddr(struct aio_slot *slot, const char *ip, 
        unsigned short port);

/* The argument ip is in network byte order */
void aio_setdstaddr2(struct aio_slot *slot, unsigned int ip, 
        unsigned short port);

/* Both ip and port are in network byte order */
void aio_setdstaddr3(struct aio_slot *slot, unsigned int ip, 
        unsigned short port);

/* Default max toread size is 4k */
int aio_settoread(struct aio_slot *slot, unsigned size);

int aio_setread(struct aio_slot *slot, void (*fn)(struct aio_slot *), 
        unsigned permanence, unsigned timeout);

void aio_setclose(struct aio_slot *slot, void (*fn)(struct aio_slot *));

int aio_listen(struct aio_slot *slot, void (*fn)(struct aio_slot *));

int aio_connect(struct aio_slot *slot, 
        void (*fn)(struct aio_slot *), unsigned short timeout);

struct aio_buf* aio_read(struct aio_slot *slot, int autorm);

void aio_write(struct aio_slot *slot, 
        struct aio_buf *sb, void (*fn)(struct aio_slot *));

#ifdef _AIO_USE_OPENSSL
/* default authtype : SSL_VERIFY_NONE */
void aio_settlsauth(struct aio_slot *slot, unsigned char authtype);

int aio_tlslisten(struct aio_slot *slot, void (*fn)(struct aio_slot *),
        const char *cacrt, const char *srvcrt, const char *srvkey);
#endif

#ifdef __cplusplus
}
#endif

#endif /* __AIO_SLOT_H */

